#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "global.h"
#include "build.h"

//---------------------------------------------------------
//扫描所有h头文件,得到其中最近修改的头文件的修改时间
static int get_mru_htime(void);
//获取所有需要重新编译的C文件组成的字符串
static char *get_recompiled(void);
//获取所有O文件组成的字符串
static char *get_ostr(void);
//获得编译所需最终字符串
static char *compile_str(void);
//获得链接所需最终字符串
static char *link_str(void);
//---------------------------------------------------------

/*
 *扫描所有h头文件,得到其中最近修改的头文件的修改时间
 */
static int get_mru_htime(void){
    int mru_htime = 0; 
    struct stat hbuf; 
    H_NAME *node = H_HEAD;
    while(node){
       if(stat(node->hname,&hbuf) < 0) 
           err_quit(STAT_ERR);
       if(hbuf.st_mtime > mru_htime)
           mru_htime = hbuf.st_mtime;
       node = node->next;
    }
    return mru_htime;
}

/*
 * 根据要求获取需要重新编译的c文件名,要求如下:
 *          1.C文件没有对应的目标文件时,需要重新编译
 *          2.C文件比对应的目标文件最后修改时间晚时需重新编译
 *          3.H头文件中只要有一个最后修改时间大于o文件
 */
static char *get_recompiled(void){
    int len = 2,mru_htime = get_mru_htime();
    char *recompiled_str = NULL;
    struct stat cbuf,obuf; 
    CO_NAME *node = CO_HEAD;
    while(node){
        if(stat(node->cname,&cbuf) < 0)
            err_quit(STAT_ERR);
        obuf.st_mtime = cbuf.st_mtime; //设置o文件的最后修改时间=c文件的最后修改时间,便于下面比较
        if(node->oname[0] != '\0')     //存在对应o文件时,获取其时间
            if(stat(node->oname,&obuf) < 0) 
                err_quit(STAT_ERR);
        /*
         * 1.node->oname[0] == '\0'         ==>  不存在o文件
         * 2.cbuf.st_mtime > obuf.st_mtime  ==>  c文件最后修改时间>o文件
         * 3.mru_htime > obuf.st_mtime      ==>  头文件中只要有一个最后修改时间大于o文件
         */
        if(node->oname[0] == '\0' || cbuf.st_mtime > obuf.st_mtime || mru_htime > obuf.st_mtime){
            len += (strlen(node->cname) + 1);
            node->compile_flag = 1;
        }else
            node->compile_flag = 0;//标记无需编译
        node = node->next;
    }
    if(len == 2)//无需重新编译
        return NULL;
    recompiled_str = (char *)malloc(len);
    strcpy(recompiled_str," ");
    node = CO_HEAD;
    while(node){
        if(node->compile_flag){
            strcat(recompiled_str,node->cname);
            strcat(recompiled_str," ");
        }
        node = node->next;
    }
    return recompiled_str;
}

/*
 * 得到编译生成的o文件的文件名组成的字符串,以便链接
 */
static char *get_ostr(void){  
    CO_NAME *node = CO_HEAD;
    //判断可执行文件是否存在并最新
    if(access(ENAME,F_OK) == 0){
        struct stat ebuf,obuf; 
        if(stat(ENAME,&ebuf) < 0)
            err_quit(STAT_ERR);
        int need_relink = 0;
        while(node){
            if(stat(node->oname,&obuf) < 0)
                err_quit(STAT_ERR);
            if(ebuf.st_mtime < obuf.st_mtime){
                need_relink = 1;
                break;
            }
            node = node->next;
        }
        if(!need_relink)
            return NULL;
    }
    int len = 2;
    node = CO_HEAD;
    while(node){
        len += (strlen(node->cname) + 1);
        node = node->next;
    }
    char *all_ostr = (char *)malloc(len);
    strcpy(all_ostr," ");
    node =CO_HEAD;
    while(node){
        strcat(all_ostr,node->cname);
        all_ostr[strlen(all_ostr) - 1] = 'o';
        strcat(all_ostr," ");
        node = node->next;
    }
    return all_ostr;
}

/*
 * 组合前面各种字符串,生成最终编译时gcc使用的字符串,该字符串被
 * system调用,进而执行字符串内容.
 */
static char *compile_str(void){
    char *packed_str = NULL;
    int flags_len = (FLAGS? strlen(FLAGS):0);
    char *recompiled_str = get_recompiled();
    if(!recompiled_str)
        return NULL;
    //7 = length("gcc -c" + "\0")
    packed_str = (char *)malloc(7 + flags_len + strlen(recompiled_str));
    strcpy(packed_str,"gcc -c");
    if(FLAGS)
        strcat(packed_str,FLAGS);
    strcat(packed_str,recompiled_str);
    return packed_str;
}

/*
 * 组合前面各种字符串,生成最终链接时gcc使用的字符串,该字符串被
 * system调用,进而执行字符串内容.
 */
static char *link_str(void){ 
    char *packed_str = NULL;
    char *all_ostr = get_ostr();
    if(!all_ostr)
        return NULL;
    int flags_len = (FLAGS? strlen(FLAGS):0);
    int libs_len = (LIBS? strlen(LIBS):0);
    packed_str = (char *)malloc(8 + flags_len + strlen(ENAME) + strlen(all_ostr) + libs_len);
    strcpy(packed_str,"gcc -o ");
    strcat(packed_str,ENAME);
    if(FLAGS)
        strcat(packed_str,FLAGS);
    strcat(packed_str,all_ostr);
    if(LIBS)
        strcat(packed_str,LIBS);
    return packed_str;
}

/*
 * 使用生成的字符串执行编译与链接
 */
void compile_link(void){
    int status;

    char *c_str = compile_str();
    printf("编译:       %s\n\n",c_str? c_str:"源文件已编译完成,无需重新编译");
    if(c_str){
        status = system(c_str);
        //出错处理，分三种错误依次处理
        if(status == -1  || !WIFEXITED(status) || WEXITSTATUS(status) != 0) 
            err_quit(SYS_FAULT,0);
    }

    CO_NAME *node = CO_HEAD;
    while(node){//填充链表,存储生成的目标文件名
        if(node->oname[0] == '\0'){
            strcpy(node->oname,node->cname);
            node->oname[strlen(node->oname) - 1] = 'o';
        }
        node = node->next;
    }

    char *l_str = link_str();
    printf("链接生成:   %s\n\n",l_str? l_str:"当前可执行文件为最新,无需重新生成");
    if(l_str){
        status = system(l_str);
        if(status == -1  || !WIFEXITED(status) || WEXITSTATUS(status) != 0) 
            err_quit(SYS_FAULT,1);
    }
}
